package com.godenwater.recv.server.szy206;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.gov.mwr.szy206.IMessage;
import cn.gov.mwr.szy206.IMessageHeader;
import cn.gov.mwr.szy206.SzyMessage;
import cn.gov.mwr.szy206.SzyMessageBody;
import cn.gov.mwr.szy206.SzyMessageHeader;
import cn.gov.mwr.szy206.SzyParser;
import cn.gov.mwr.szy206.utils.ByteUtil;
 

/**
 * 报文解码类，作为上行报文的解码类，此类必须实现对所有消息体的封闭细节
 * 
 * @ClassName: YfServerDataDecoder
 * @Description: 处理断包和粘包的实现类
 * @author lipujun
 * @date Mar 2, 2013
 * 
 */
public class ServerDataDecoder extends CumulativeProtocolDecoder {
	private static final AttributeKey BUF_BYTE = new AttributeKey(
			ServerDataDecoder.class, "BUF_KEY");

	private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	public Context getContext(IoSession session) {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx == null) {
			ctx = new Context();
			session.setAttribute(CONTEXT, ctx);
		}
		return ctx;
	}

	private class Context {
		// 状态变量
		private final IoBuffer innerBuffer;

		public Context() {
			innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
		}
	}

	/**
	 * 这个办法的返回值是重点：
	 * 
	 * 1、当内容正好时，返回false，告诉父类可以进行下一批消息的处理
	 * 
	 * 2、内容不符时，需要下一批消息的内容，此时返回false
	 * ，如许父类会将内容放进IoSession中，等下次数据来后就主动拼装再交给本类的doDecode
	 * 
	 * 3、当内容多时，返回true，因为需要再将本批数据进行读取，父类会将残剩的数据再次推送本类的doDecode
	 */
	@Override
	protected boolean doDecode(IoSession session, IoBuffer buffer,
			ProtocolDecoderOutput out) throws Exception {
		// TODO Auto-generated method stub
		logger.info(">> HEX : " + ByteUtil.toHexString(buffer.array()));

		int size = 0;
		int rcrlen = 0;
		int bodyLengthLen = 0;

		if (buffer.remaining() > 0) {// 表示缓冲区中有数据
			System.out.println("------buffer.remaining() ---------"
					+ buffer.remaining());

			buffer.mark();// 标记当前位置，以便reset

			byte[] mode = new byte[1];
			buffer.get(mode);

			IMessageHeader header;
			if (mode[0] == (byte) 0x68) {
				header = new SzyMessageHeader();
 
				header.setStartBit(mode);

				byte[] datalen = new byte[1];
				buffer.get(datalen);
				header.setBodySize(datalen);

				byte[] startBodyBit = new byte[1];
				buffer.get(startBodyBit);
				header.setBodyStartBit(startBodyBit);

				rcrlen = 1;
				bodyLengthLen = SzyParser.parseDataLen(datalen);

			} else {
				// 处理DTU设备上线时的登录信息，发的是DTU编号和手机号
				// buffer.get(buffer.array().length);
				System.out.println(">> 非协议报文 ");
				buffer.get();
				return true;
			}

			size = bodyLengthLen + 1 + rcrlen;

			// 若是消息内容的长度不敷则直接返回true
			if (size > buffer.remaining()) {// 若是消息大小与缓冲区中的内容大小不匹配，则重置，相当于不读取size
				 System.out.println("------size > buffer.remaining() ---------"
				 + size + "  " + buffer.remaining());
				buffer.reset();
				return false;// 接管新数据，以拼凑成完全数据
			} else {
				 System.out
				 .println("------size < buffer.remaining() ---------bodyLengthLen "
				 + bodyLengthLen
				 + " size "
				 + size
				 + "  "
				 + buffer.remaining());
				byte[] bodyContent = new byte[bodyLengthLen];// 根据前面header的内容来获取长度
				buffer.get(bodyContent);

				// 校验码
				byte[] crc = new byte[rcrlen];
				// 终止符
				byte[] eof = new byte[1];

				// 水资源协议
				buffer.get(crc);
				buffer.get(eof);

				// 组装消息
				IMessage message = new SzyMessage();
				message.setHeader(header);

				// 根据功能码，构建body体
				SzyMessageBody body = new SzyMessageBody();
				body.setContents(bodyContent);
				message.setBody(body);
				message.setEOF(eof[0]);
				message.setCRC(crc);

				out.write(message);

				if (buffer.remaining() > 0) {// 若是读取内容后还粘了包，就让父类再给解析一次，返回true进行下一次解析
					return true;
				}
			}
		}
		return false;// 处理成功，让父类进行接管下个包
	}

	public void dispose(IoSession session) throws Exception {
		// TODO Auto-generated method stub

	}

	public void finishDecode(IoSession session, ProtocolDecoderOutput out)
			throws Exception {
		// TODO Auto-generated method stub

	}

}
